<?php
/**
 * Created by PhpStorm.
 * User: zd
 * Date: 2018/8/14
 * Time: 15:48
 */

namespace app\api\service;


use app\lib\enum\ScopeEnum;
use app\lib\exception\TokenException;
use app\lib\exception\WechatException;
use \app\api\model\ThirdApp;
use think\Exception;
use app\api\model\User as UserModel;

class UserToken extends Token
{
    protected $code;
    protected $wxAppID;
    protected $component_appid;
    protected $component_access_token;
    protected $wxLoginUrl;
    protected $manager_id;
    protected $end_time;

    public function __construct($code,$id)
    {
        $info = ThirdApp::getDetailUserInfoByMini($id);
        $appid = $info->mini->authorizer_appid;
        $this->manager_id = $id;
        $this->end_time = $info->end_time;
        $this->code = $code;
        $this->wxAppID = $appid;
        $this->component_appid = config('mini.AppID');
        $this->component_access_token = (new Mini())->getComponentValue();//获取component_access_token的值
        $this->wxLoginUrl = sprintf(config('mini.login_url'),$this->wxAppID,$this->code,$this->component_appid,$this->component_access_token);
    }

    /**
     * 获取token
     * @return array
     * @throws Exception
     * @throws TokenException
     * @throws WechatException
     */
    public function get(){

        $result  = curl_get($this->wxLoginUrl);
        $wxResult = json_decode($result,true);
        if (empty($wxResult)){
            throw new Exception('获取session_key,openID是异常，微信内部错误');
        }else{
            $loginFail = array_key_exists('errcode',$wxResult);
            if ($loginFail){
                $this->processLoginError($wxResult);
            }else{
                return $this->grantToken($wxResult);
            }
        }
    }

    /**
     * 返回token
     * @param array $wxResult
     * @return array
     * @throws TokenException
     */
    public function grantToken( array $wxResult){
        //发放token
        //1.先查询数据库是否有该用户
        //2.如果存在，则不处理。如不存在，则向数据库中插入一个user
        //3.生成令牌，准备缓存，将令牌写入缓存(键值对)
        //4.把key令牌返回到客户端
        //key：令牌
        //value：[$wxResult,$uid,scope]--------------scope该令牌的作用域
        $auth = 'unauthorized';//默认为用户未授权
        $time = 'expired';//默认用户过期
        $userInfo = '';
        $openid = $wxResult['openid'];
        $session_key = $wxResult['session_key'];
        $user = UserModel::getUserInfoByOpenID($openid);
        if (empty($user)){
            $uid = UserModel::createUserByOpenId($this->manager_id,$openid,$session_key);
        }else{
            $uid = $user->id;
            if(!empty($user->nickname)){
                $auth = 'authorized';
            }
            $userInfo = $user->visible(['avatar_url','nickname','id',]);
        }
        if ($this->end_time > time()){
            $time = 'notExpired';//未过期
        }
        $caheValue = $this->prepareCachedValue($wxResult,$uid);
        $token = $this->saveToCache($caheValue);
        return [$token,$auth,$time,$userInfo];
    }
    /**
     * 生成并缓存token
     * @param $caheValue
     * @return string
     * @throws TokenException
     */
    private function saveToCache($caheValue){
       $key =  self::generateToken();
       $value = json_encode($caheValue);
       $expire_in = config('setting.token_expire_in');
       $result = cache($key,$value,$expire_in);
       if (!$result){
           throw new TokenException([
               'msg'=>'token缓存异常',
               'error_code'=>10005
           ]);
       }
       return $key;
    }

    /**
     * 要准备的缓存
     * @param array $wxResult
     * @param $uid
     * @return array
     */
    private function prepareCachedValue(array $wxResult,$uid){
        $caheValue = $wxResult;
        $caheValue['manager_id'] = $this->manager_id;
        $caheValue['uid']=$uid;
        $caheValue['scope'] = ScopeEnum::USER;//权限最高
        return $caheValue;
    }
    /**
     * 微信返回错误处理函数
     * @param $wxResult
     * @throws WechatException
     */
    private function  processLoginError(array $wxResult){
        throw new WechatException([
            'msg' => $wxResult['errmsg'],
            'error_code' => $wxResult['errcode']
        ]);
    }
}